﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once

#include "GmFamilyService.h"

#include "IGraphicsNodeReference.h"
#include "Vector3d.h"
#include "FamilySolverResult.h"
#include "FamilyConstraintReferenceType.h"

namespace gcmp
{
    class IDocument;
    class IElement;
    class Matrix4d;
    class IGraphicsNode;
    class Coordinate3d;
    class ElementId;
}

namespace gfam
{
    struct GSolverData;

    /// \brief 几何对象策略
    ///
    /// 用于约束过程中提供几何数据和更新对应的几何对象 
    class GM_FAMILY_SERVICE_EXPORT FamilyGeometryStrategy
    {
    public:

        /// \brief 获取引用的图形节点
        ///
        /// \param pElement 引用的几何对象
        /// \param nodeReference 图形节点引用
        /// \return 该节点上的带有变换矩阵的图形节点
        virtual gcmp::OwnerPtr<gcmp::GraphicsNodeAndTransform> GetGeometry(const gcmp::IElement* pElement, const gcmp::IGraphicsNodeReference& nodeReference);

        /// \brief 更新几何对象
        ///
        /// \param pElement 待更新的几何对象
        /// \param node 图形节点数据，用来更新Element
        /// \return 返回图形节点的引用
        virtual bool UpdateGeometry(gcmp::IElement* pElement, gcmp::OwnerPtr<gcmp::IGraphicsNode> node);

    };


    /// \brief 草图约束解算类
    class GM_FAMILY_SERVICE_EXPORT FamilySketchSolver
    {
    public:
        /// \brief 构造函数
        /// 
        /// 会创建默认的几何对象策略
        /// \param pDocument 文档
        FamilySketchSolver(gcmp::IDocument* pDocument);

        /// \brief 构造函数
        /// 
        /// \param pDocument 文档
        /// \param strategy 自定义的几何对象策略
        FamilySketchSolver(gcmp::IDocument* pDocument, gcmp::OwnerPtr<FamilyGeometryStrategy> strategy);

        /// \brief 析构函数
        ~FamilySketchSolver();

        /// \brief 设置坐标系
        ///
        /// 添加的几何对象会投影到这个坐标系中
        void SetCoordinate(const gcmp::Coordinate3d& coord);

        /// \brief 获取坐标系
        ///
        /// \return 坐标系
        const gcmp::Coordinate3d & GetCoordinate()const;

        /// \brief 添加约束对象
        ///
        /// \param constraintIds 约束对象Id数组 
        void AddConstraints(const std::vector<gcmp::ElementId>& constraintIds);

        /// \brief 添加约束对象
        ///
        /// \param constraintIds 约束对象Id集合 
        void AddConstraints(const gcmp::UnorderedElementIdSet& constraintIds);

        /// \brief 获取添加的约束对象
        ///
        /// \return 约束对象Id集合 
        const std::vector<gcmp::ElementId>& GetConstraints() { return m_constraintIds; }

        /// \brief 添加几何对象
        ///
        /// 被约束引用的对象如不在添加的几何对象中，引用对象做固定处理
        /// \param geometryIds 几何对象Id数组 
        void AddGeometries(const std::vector<gcmp::ElementId>& geometryIds);

        /// \brief 添加几何对象
        ///
        /// 被约束引用的对象如不在添加的几何对象中，引用对象做固定处理
        /// \param geometryIds 几何对象Id集合 
        void AddGeometries(const gcmp::UnorderedElementIdSet& geometryIds);


        /// \brief 获取添加的几何对象
        ///
        /// \return 几何对象Id集合 
        const std::vector<gcmp::ElementId>& GetGeometries() { return m_geometryIds; }

        /// \brief 进行约束解算
        ///
        /// \return 约束解算结果
        FamilySolverResult Solve();

        /// \brief 进行约束解算，可以指定临时固定的约束
        ///
        /// \param tempPinnedObjects 需要临时固定的引用对象
        /// \return 约束解算结果
        FamilySolverResult Solve(const std::map<gcmp::ElementId, gfam::FamilyConstraintReferenceType>& tempPinnedObjects);

        /// \brief 进行动态约束解算
        ///
        /// 一般用于拖拽时调用
        /// \param nodeReference 拖拽的对象引用
        /// \param movePt 移动的起点
        /// \param moveDelta 移动的距离矢量
        /// \return 约束解算结果
        FamilySolverResult DynamicSolve(const gcmp::IGraphicsNodeReference& nodeReference, const gcmp::Vector3d& movePt, const gcmp::Vector3d& moveDelta);


        /// \brief 进行动态约束解算
        ///
        /// 一般用于拖拽时调用
        /// \param nodeReference 拖拽对象图形引用
        /// \param type 引用类型（用于确定修改图形的哪一部分）
        /// \param movePt 移动的起点
        /// \param moveDelta 移动的距离矢量
        /// \return 约束解算结果
        FamilySolverResult DynamicSolve(const gcmp::IGraphicsNodeReference& nodeReference, FamilyConstraintReferenceType type, const gcmp::Vector3d& movePt, const gcmp::Vector3d& moveDelta);


        /// \brief 获取解算失败的约束
        ///
        /// 约束解算失败后可以用来获取失败的约束对象做一些处理
        /// \return 解算失败的约束数组
        std::vector<gcmp::ElementId> GetFailedConstraints();


        /// \brief 设置几何对象策略
        ///
        /// \param strategy 新的几何对象策略
        /// \return 旧的几何对象策略
        gcmp::OwnerPtr<FamilyGeometryStrategy> SetStrategy(gcmp::OwnerPtr<FamilyGeometryStrategy> strategy);

        /// \brief 获取几何对象策略
        ///
        /// \return 几何对象策略指针
        FamilyGeometryStrategy* GetStrategy();


    private:

        gcmp::Coordinate3d m_coordinate;
        gcmp::IDocument* m_pDocument;

        std::vector<gcmp::ElementId> m_constraintIds;
        std::vector<gcmp::ElementId> m_geometryIds;

        gcmp::OwnerPtr<FamilyGeometryStrategy> m_strategy;
        gcmp::OwnerPtr<GSolverData> m_solverData;

    };
}

